import json
import logging
import time
import os
from openai import OpenAI
import requests
import numpy as np
from django.conf import settings
from django.db import connection, transaction
from django.utils import timezone
from .models import UserPreference, BoxFeature, UserRecommendation, RecommendationLog
from analytics.models import UserBehavior
from accounts.models import User

logger = logging.getLogger('recommendation')

class DeepSeekRecommendationService:
    """DeepSeek AI推荐服务"""
    
    def __init__(self, api_key=None):
        """初始化推荐服务"""
        self.api_key = api_key or settings.DEEPSEEK_API_KEY

        # 清除可能影响OpenAI客户端的环境变量
        proxy_env_vars = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy', 'ALL_PROXY', 'all_proxy']
        saved_env = {}
        
        # 保存当前环境变量
        for var in proxy_env_vars:
            if var in os.environ:
                saved_env[var] = os.environ[var]
                del os.environ[var]
                logger.info(f"临时移除环境变量 {var}")
        
        # 尝试初始化客户端
        try:
            logger.info("正在初始化OpenAI客户端，API密钥长度：%d", len(self.api_key) if self.api_key else 0)
            self.client = OpenAI(
                api_key=self.api_key,
                base_url="https://api.deepseek.com"
            )
            logger.info("OpenAI客户端初始化成功")
        except TypeError as e:
            logger.error(f"初始化OpenAI客户端错误: {str(e)}")
            # 尝试降级处理，仅使用基本参数
            try:
                self.client = OpenAI(api_key=self.api_key)
                logger.info("使用基本参数初始化OpenAI客户端成功")
                # 手动设置base_url
                if hasattr(self.client, 'base_url'):
                    self.client.base_url = "https://api.deepseek.com"
                    logger.info("手动设置base_url成功")
            except Exception as e2:
                logger.error(f"尝试降级初始化OpenAI客户端也失败: {str(e2)}")
                self.client = None
        except Exception as e:
            logger.error(f"初始化OpenAI客户端时发生未知错误: {str(e)}")
            self.client = None
        
        # 恢复环境变量
        for var, value in saved_env.items():
            os.environ[var] = value
    
    def _get_user_behavior_data(self, user_id, limit=50):
        """获取用户行为数据"""
        behaviors = UserBehavior.objects.filter(
            user_id=user_id
        ).order_by('-create_time')[:limit]
        
        return list(behaviors.values('action_type', 'object_type', 'object_id', 'create_time'))
    
    def _get_user_order_history(self, user_id, limit=20):
        """获取用户订单历史"""
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT oi.box_id, bb.name, oi.price, o.create_time
                FROM tb_order o
                JOIN tb_order_item oi ON o.order_id = oi.order_id
                JOIN tb_blind_box bb ON oi.box_id = bb.box_id
                WHERE o.user_id = %s AND o.status IN (2, 3, 4)
                ORDER BY o.create_time DESC
                LIMIT %s
            """, [user_id, limit])
            
            orders = []
            for row in cursor.fetchall():
                orders.append({
                    'box_id': row[0],
                    'box_name': row[1],
                    'price': float(row[2]),
                    'order_time': row[3].isoformat() if row[3] else None
                })
            
            return orders
    
    def _get_box_details(self, box_id=None, limit=100):
        """获取盲盒详细信息"""
        with connection.cursor() as cursor:
            if box_id:
                cursor.execute("""
                    SELECT box_id, name, description, category, original_price, discount_price, 
                           image_url, stock
                    FROM tb_blind_box
                    WHERE box_id = %s AND status = 1
                """, [box_id])
            else:
                cursor.execute("""
                    SELECT box_id, name, description, category, original_price, discount_price, 
                           image_url, stock
                    FROM tb_blind_box
                    WHERE status = 1
                    ORDER BY update_time DESC
                    LIMIT %s
                """, [limit])
            
            boxes = []
            for row in cursor.fetchall():
                boxes.append({
                    'box_id': row[0],
                    'name': row[1],
                    'description': row[2],
                    'category': row[3],
                    'price': float(row[4]),
                    'discount_price': float(row[5]),
                    'image_url': row[6],
                    'style': '精美礼盒',  # 默认风格
                    'theme': '精选盲盒',  # 默认主题
                    'stock': row[7]
                })
            
            return boxes
    
    def _analyze_user_preference(self, user_id):
        """使用DeepSeek分析用户偏好"""
        # 获取用户行为和订单历史
        behaviors = self._get_user_behavior_data(user_id)
        orders = self._get_user_order_history(user_id)
        
        # 构建提示文本
        prompt = f"""
        作为智能推荐系统，请根据以下用户行为和订单历史分析用户的偏好特征。
        你需要输出JSON格式的结果，包含以下字段：
        1. preferred_categories: 用户喜欢的盲盒类别列表（最多3个），每个类别附带一个0到1的喜好度分数
        2. preferred_themes: 用户喜欢的盲盒主题列表（最多3个），每个主题附带一个0到1的喜好度分数
        3. preferred_styles: 用户喜欢的盲盒风格列表（最多3个），每个风格附带一个0到1的喜好度分数
        4. price_sensitivity: 用户对价格的敏感度（0-1，越高表示越敏感）
        5. novelty_seeking: 用户对新奇事物的追求程度（0-1，越高表示越追求新奇）
        6. collection_tendency: 用户收集完整系列的倾向（0-1，越高表示越喜欢收集系列）
        
        用户行为数据：
        {json.dumps(behaviors, ensure_ascii=False, indent=2)}
        
        用户订单历史：
        {json.dumps(orders, ensure_ascii=False, indent=2)}
        
        仅输出JSON格式的分析结果，不要有其他文字。
        """
        
        try:
            # 调用DeepSeek API
            response = self.client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": "你是一个智能推荐系统分析师，擅长分析用户偏好并输出规范的JSON数据"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3,
                max_tokens=1000
            )
            
            if response:
                content = response.choices[0].message.content
                
                # 提取JSON部分
                try:
                    start_idx = content.find('{')
                    end_idx = content.rfind('}') + 1
                    if start_idx != -1 and end_idx != -1:
                        json_str = content[start_idx:end_idx]
                        preference_data = json.loads(json_str)
                        return preference_data
                except Exception as e:
                    logger.error(f"解析DeepSeek响应JSON失败: {str(e)}")
                    return {}
            else:
                logger.error("DeepSeek API请求失败：未获得响应")
                return {}
        except Exception as e:
            logger.error(f"调用DeepSeek API异常: {str(e)}")
            return {}
    
    def _analyze_box_features(self, box_data):
        """使用DeepSeek分析盲盒特征"""
        # 构建提示文本
        prompt = f"""
        作为智能推荐系统，请分析以下盲盒商品的特征。
        你需要输出JSON格式的结果，包含以下字段：
        1. category_vector: 盲盒类别的特征向量，用0-1表示不同类别的权重
        2. theme_vector: 盲盒主题的特征向量，用0-1表示不同主题的权重
        3. style_vector: 盲盒风格的特征向量，用0-1表示不同风格的权重
        4. price_level: 价格水平（0-1，越高表示越贵）
        5. novelty_level: 新奇程度（0-1，越高表示越新奇）
        6. collectibility: 收藏价值（0-1，越高表示收藏价值越高）
        
        盲盒数据：
        {json.dumps(box_data, ensure_ascii=False, indent=2)}
        
        仅输出JSON格式的分析结果，不要有其他文字。
        """
        
        try:
            # 调用DeepSeek API
            response = self.client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": "你是一个智能推荐系统分析师，擅长分析商品特征并输出规范的JSON数据"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.2,
                max_tokens=1000
            )
            
            if response:
                content = response.choices[0].message.content
                
                # 提取JSON部分
                try:
                    start_idx = content.find('{')
                    end_idx = content.rfind('}') + 1
                    if start_idx != -1 and end_idx != -1:
                        json_str = content[start_idx:end_idx]
                        feature_data = json.loads(json_str)
                        return feature_data
                except Exception as e:
                    logger.error(f"解析DeepSeek响应JSON失败: {str(e)}")
                    return {}
            else:
                logger.error("DeepSeek API请求失败：未获得响应")
                return {}
        except Exception as e:
            logger.error(f"调用DeepSeek API异常: {str(e)}")
            return {}
    
    def _get_recommendation_reason(self, user_preference, box_feature, box_data):
        """生成推荐理由"""
        # 构建提示文本
        prompt = f"""
        作为智能推荐系统，请为以下用户生成一条简短的推荐理由，说明为什么这个盲盒商品适合该用户。
        理由应该简洁有力，不超过30个字，突出最吸引用户的1-2个特点。
        
        用户偏好：
        {json.dumps(user_preference, ensure_ascii=False, indent=2)}
        
        盲盒特征：
        {json.dumps(box_feature, ensure_ascii=False, indent=2)}
        
        盲盒信息：
        {json.dumps(box_data, ensure_ascii=False, indent=2)}
        
        仅输出推荐理由文本，不要有其他文字。
        """
        
        try:
            # 调用DeepSeek API
            response = self.client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": "你是一个智能推荐系统，擅长生成简短有力的推荐理由"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7,
                max_tokens=100
            )
            
            if response:
                content = response.choices[0].message.content
                
                # 清理响应文本
                reason = content.strip().strip('"\'')
                if len(reason) > 255:
                    reason = reason[:252] + "..."
                
                return reason
            else:
                logger.error("DeepSeek API请求失败：未获得响应")
                return "匹配你的喜好"
        except Exception as e:
            logger.error(f"调用DeepSeek API异常: {str(e)}")
            return "匹配你的喜好"
    
    def _compute_recommendation_score(self, user_preference, box_feature):
        """计算推荐得分"""
        score = 0.0
        weight_sum = 0.0
        
        # 类别匹配得分
        if user_preference.get('preferred_categories') and box_feature.get('category_vector'):
            category_score = 0.0
            for category in user_preference['preferred_categories']:
                cat_name = category['category']
                cat_score = category['score']
                if cat_name in box_feature['category_vector']:
                    category_score += cat_score * box_feature['category_vector'][cat_name]
            score += category_score * 0.3
            weight_sum += 0.3
        
        # 主题匹配得分
        if user_preference.get('preferred_themes') and box_feature.get('theme_vector'):
            theme_score = 0.0
            for theme in user_preference['preferred_themes']:
                theme_name = theme['theme']
                theme_score = theme['score']
                if theme_name in box_feature['theme_vector']:
                    theme_score += theme_score * box_feature['theme_vector'][theme_name]
            score += theme_score * 0.25
            weight_sum += 0.25
        
        # 风格匹配得分
        if user_preference.get('preferred_styles') and box_feature.get('style_vector'):
            style_score = 0.0
            for style in user_preference['preferred_styles']:
                style_name = style['style']
                style_score = style['score']
                if style_name in box_feature['style_vector']:
                    style_score += style_score * box_feature['style_vector'][style_name]
            score += style_score * 0.2
            weight_sum += 0.2
        
        # 价格敏感度
        if 'price_sensitivity' in user_preference and 'price_level' in box_feature:
            # 价格敏感度高的用户偏好价格低的商品
            price_match = 1.0 - (user_preference['price_sensitivity'] * box_feature['price_level'])
            score += price_match * 0.1
            weight_sum += 0.1
        
        # 新奇性
        if 'novelty_seeking' in user_preference and 'novelty_level' in box_feature:
            novelty_match = user_preference['novelty_seeking'] * box_feature['novelty_level']
            score += novelty_match * 0.1
            weight_sum += 0.1
        
        # 收藏倾向
        if 'collection_tendency' in user_preference and 'collectibility' in box_feature:
            collection_match = user_preference['collection_tendency'] * box_feature['collectibility']
            score += collection_match * 0.05
            weight_sum += 0.05
        
        # 归一化得分
        if weight_sum > 0:
            score = score / weight_sum
        
        return score
    
    def _generate_box_features(self):
        """为所有盲盒生成特征向量"""
        # 获取所有盲盒
        boxes = self._get_box_details()
        features_updated = 0
        
        for box in boxes:
            box_id = box['box_id']
            
            # 检查是否已存在特征
            try:
                feature = BoxFeature.objects.get(box_id=box_id)
                # 如果上次更新超过7天，则更新特征
                if (timezone.now() - feature.update_time).days < 7:
                    continue
            except BoxFeature.DoesNotExist:
                feature = BoxFeature(box_id=box_id)
            
            # 分析盲盒特征
            feature_data = self._analyze_box_features(box)
            if not feature_data:
                continue
            
            # 更新特征数据
            feature.feature_data = json.dumps(feature_data, ensure_ascii=False)
            feature.update_time = timezone.now()
            feature.save()
            features_updated += 1
            
            # 限制API请求频率
            time.sleep(0.5)
        
        return features_updated
    
    def generate_user_recommendations(self, user_id=None, limit=8):
        """生成用户推荐"""
        if user_id:
            # 为单个用户生成推荐
            users = User.objects.filter(id=user_id)
        else:
            # 为所有活跃用户生成推荐
            one_month_ago = timezone.now() - timezone.timedelta(days=30)
            users = User.objects.filter(
                last_login__gte=one_month_ago
            ).order_by('-last_login')[:100]  # 只为最近100个活跃用户生成
        
        recommendations_generated = 0
        
        # 确保所有盲盒都有特征向量
        self._generate_box_features()
        
        for user in users:
            # 分析用户偏好
            preference_data = self._analyze_user_preference(user.id)
            if not preference_data:
                continue
            
            # 保存或更新用户偏好
            try:
                preference = UserPreference.objects.get(user_id=user.id)
            except UserPreference.DoesNotExist:
                preference = UserPreference(user_id=user.id)
            
            preference.preference_data = json.dumps(preference_data, ensure_ascii=False)
            preference.update_time = timezone.now()
            preference.save()
            
            # 获取所有盲盒特征
            box_features = BoxFeature.objects.all()
            
            # 计算推荐得分
            recommendations = []
            for feature in box_features:
                box_id = feature.box_id
                box_feature_data = json.loads(feature.feature_data)
                
                # 获取盲盒详情
                box_data = self._get_box_details(box_id=box_id)
                if not box_data:
                    continue
                box_data = box_data[0]
                
                # 计算推荐得分
                score = self._compute_recommendation_score(preference_data, box_feature_data)
                
                # 获取推荐理由
                reason = self._get_recommendation_reason(preference_data, box_feature_data, box_data)
                
                recommendations.append({
                    'box_id': box_id,
                    'score': score,
                    'reason': reason,
                    'box_data': box_data
                })
            
            # 按得分排序并取最高的N个
            recommendations.sort(key=lambda x: x['score'], reverse=True)
            top_recommendations = recommendations[:limit]
            
            # 保存推荐结果
            with transaction.atomic():
                # 删除之前的推荐
                UserRecommendation.objects.filter(user_id=user.id).delete()
                
                # 插入新推荐
                for rank, rec in enumerate(top_recommendations, 1):
                    UserRecommendation.objects.create(
                        user_id=user.id,
                        box_id=rec['box_id'],
                        score=rec['score'],
                        rank=rank,
                        reason=rec['reason'],
                        create_time=timezone.now()
                    )
            
            recommendations_generated += 1
            
            # 记录推荐日志
            RecommendationLog.objects.create(
                user_id=user.id,
                recommendation_count=len(top_recommendations),
                log_type='generate',
                create_time=timezone.now()
            )
            
            # 限制API请求频率
            time.sleep(1)
        
        return recommendations_generated
    
    def get_user_recommendations(self, user_id, limit=8):
        """获取用户推荐列表"""
        # 检查是否有现有推荐
        recommendations = list(UserRecommendation.objects.filter(
            user_id=user_id
        ).order_by('rank')[:limit])
        
        # 如果没有推荐或推荐已过期，则生成新推荐
        if not recommendations:
            self.generate_user_recommendations(user_id=user_id, limit=limit)
            recommendations = list(UserRecommendation.objects.filter(
                user_id=user_id
            ).order_by('rank')[:limit])
        else:
            # 检查推荐是否已过期（超过24小时）
            oldest_rec = recommendations[0] if recommendations else None
            if oldest_rec and (timezone.now() - oldest_rec.create_time).days >= 1:
                self.generate_user_recommendations(user_id=user_id, limit=limit)
                recommendations = list(UserRecommendation.objects.filter(
                    user_id=user_id
                ).order_by('rank')[:limit])
        
        # 获取盲盒详情
        recommendation_data = []
        for rec in recommendations:
            box_details = self._get_box_details(box_id=rec.box_id)
            if box_details:
                box_data = box_details[0]
                recommendation_data.append({
                    'recommendation_id': rec.id,
                    'box_id': rec.box_id,
                    'box_name': box_data['name'],
                    'box_image': box_data['image_url'],
                    'price': box_data['price'],
                    'discount_price': box_data['discount_price'],
                    'category': box_data['category'],
                    'theme': box_data['theme'],
                    'score': rec.score,
                    'rank': rec.rank,
                    'reason': rec.reason,
                    'stock': box_data['stock']
                })
        
        # 记录推荐日志
        RecommendationLog.objects.create(
            user_id=user_id,
            recommendation_count=len(recommendation_data),
            log_type='view',
            create_time=timezone.now()
        )
        
        return recommendation_data
    
    def log_recommendation_click(self, recommendation_id):
        """记录推荐点击"""
        try:
            recommendation = UserRecommendation.objects.get(id=recommendation_id)
            
            # 记录点击日志
            RecommendationLog.objects.create(
                user_id=recommendation.user_id,
                box_id=recommendation.box_id,
                recommendation_id=recommendation_id,
                log_type='click',
                create_time=timezone.now()
            )
            
            # 更新点击次数
            recommendation.click_count += 1
            recommendation.last_click_time = timezone.now()
            recommendation.save()
            
            return True
        except UserRecommendation.DoesNotExist:
            return False 